// wizard.c++ 
// wizard class function bodies

#include <SDL/SDL.h>
#include <iostream>
#include <assert.h>
#include <vector>

#include "highground.h"
#include "map.h"
#include "wizard.h"

#ifdef CLIENT
#include "gfx.h"
#include "textbox.h"

// extern TextBox* messages;
#endif // CLIENT

extern Map* map;

signed char WizardColourR[]={ 127, -127, -127,   63, -127,   63, 0};
signed char WizardColourG[]={-127,  127, -127, -127,   63,   63, 0};
signed char WizardColourB[]={-127, -127,  127,   63,   63, -127, 0};

Wizard::Wizard() {
	assert(false);
}

Wizard::Wizard(const unsigned int x, const unsigned int y, 
               const unsigned int clientId) : Object(x, y), path_() {
	isAlive_ = 1;
	speed_ = 1000; // arbitrary default value
	clientId_ = clientId;
	hasCast_ = false;
	hasMoved_ = false;
	isMyTurn_ = false;
	directions_ = new Direction* [map->getWidth()];
	for (unsigned int x = 0; x < map->getWidth(); ++x) {
		directions_[x] = new Direction [map->getHeight()];
	}
	faceDirection_ = SOUTH;
	steps_ = 0;
	loadImages();
}

Wizard::~Wizard() {
	unloadImages();
}

void Wizard::draw() {
	#ifdef CLIENT
	//drawSurface(south_, x_ * SPRITE_WIDTH, y_ * SPRITE_HEIGHT);	
	if (path_.empty()) {
		if (faceDirection_ == NORTH) {
			drawToTile(north_, x_, y_);
		} else if (faceDirection_ == EAST) {
			drawToTile(east_, x_, y_);
		} else if (faceDirection_ == SOUTH) {
			drawToTile(south_, x_, y_);
		} else if (faceDirection_ == WEST) {
			drawToTile(west_, x_, y_);
		}
	} else if (path_.back() == NORTH) {
		drawToTile(walkNorth_[steps_], x_, y_ - 1);
	} else if (path_.back() == EAST) {
		drawToTile(walkEast_[steps_], x_, y_);
	} else if (path_.back() == SOUTH) {
		drawToTile(walkSouth_[steps_], x_, y_);
	} else if (path_.back() == WEST) {
		drawToTile(walkWest_[steps_], x_ - 1, y_);
	}
	#endif //CLIENT
}

void Wizard::activate() {
	
}

void Wizard::tick() {
	if (!path_.empty()) {
		++steps_;
		if (steps_ > 7) {
			if (path_.back() == NORTH) {
				--y_;
			} else if (path_.back() == EAST) {
				++x_;
			} else if (path_.back() == SOUTH) {
				++y_;
			} else if (path_.back() == WEST) {
				--x_;
			}
			path_.pop_back();
			steps_ = 0;
			if (x_ == map->getWidth() - 1 && y_ == map->getHeight() - 1 &&
				isAlive_ == 1) {
				#ifdef CLIENT
				//messages->write("You have reached the end. Well done.");
				#endif //CLIENT
				isAlive_ = 0;
			}
			if (map->getTile(1, x_, y_) != 0 && isAlive_ == 1) {
				#ifdef CLIENT
				//messages->write("You have drowned. How unfortunate.");
				#endif //CLIENT
				isAlive_ = 0;
			}
		}
	}
}

ObjectType Wizard::getObjectType() {
	return WIZARD;
}

void Wizard::calculatePaths() {
	// create array of distances (remaining) and reset array of directions
	unsigned int** distances = new unsigned int* [map->getWidth()];
	for (unsigned int x = 0; x < map->getWidth(); ++x) {
		distances[x] = new unsigned int [map->getHeight()];
		for (unsigned int y = 0; y < map->getHeight(); ++y) {
			distances[x][y] = 0;
			directions_[x][y] = NO_DIRECTION;
		}
	}
	// recursion r fun! :D
	calculatePathsRecur(distances, x_, y_, START, speed_);
	// free memory
	for (unsigned int x = 0; x < map->getWidth(); ++x) {
		delete [] distances[x];
	}
	delete [] distances;
}

bool Wizard::getIsReachable(const unsigned int x, const unsigned int y) {
	if (directions_[x][y] == NO_DIRECTION) {
		return 0;
	} else {
		return 1;
	}
}

void Wizard::setPath(unsigned int x, unsigned int y) {
	path_.clear();
	// work backwards through the direction array, recording directions
	while (1) {
		if (directions_[x][y] == NORTH) {
			path_.push_back(NORTH);
			++y;
		} else if (directions_[x][y] == EAST) {
			path_.push_back(EAST);
			--x;
		} else if (directions_[x][y] == SOUTH) {
			path_.push_back(SOUTH);
			--y;
		} else if (directions_[x][y] == WEST) {
			path_.push_back(WEST);
			++x;
		} else {
			break;
		}
	}
}

unsigned int Wizard::getPathSize() {
	return path_.size();
}

void Wizard::loadImages() {
	#ifdef CLIENT
	char fn[255];
	north_ = loadBitmap("images/wiz_north.bmp");
	tint(north_, WizardColourR[clientId_], WizardColourG[clientId_],
	     WizardColourB[clientId_]);
	east_ = loadBitmap("images/wiz_east.bmp");
	tint(east_, WizardColourR[clientId_], WizardColourG[clientId_],
	     WizardColourB[clientId_]);
	south_ = loadBitmap("images/wiz_south.bmp");
	tint(south_, WizardColourR[clientId_], WizardColourG[clientId_],
	     WizardColourB[clientId_]);
	west_ = loadBitmap("images/wiz_west.bmp");
	tint(west_, WizardColourR[clientId_], WizardColourG[clientId_],
	     WizardColourB[clientId_]);
	
	for (unsigned int i = 0; i < 8; ++i) {
		sprintf(fn, "images/wiz_walknorth%i.bmp", i);
		walkNorth_[i] = loadBitmap(fn);
		tint(walkNorth_[i], WizardColourR[clientId_], WizardColourG[clientId_],
		     WizardColourB[clientId_]);
		sprintf(fn, "images/wiz_walkeast%i.bmp", i);
		walkEast_[i] = loadBitmap(fn);
		tint(walkEast_[i], WizardColourR[clientId_], WizardColourG[clientId_],
		     WizardColourB[clientId_]);
		sprintf(fn, "images/wiz_walksouth%i.bmp", i);
		walkSouth_[i] = loadBitmap(fn);
		tint(walkSouth_[i], WizardColourR[clientId_], WizardColourG[clientId_],
		     WizardColourB[clientId_]);
		sprintf(fn, "images/wiz_walkwest%i.bmp", i);
		walkWest_[i] = loadBitmap(fn);
		tint(walkWest_[i], WizardColourR[clientId_], WizardColourG[clientId_],
		     WizardColourB[clientId_]);
	}
	#endif
}

void Wizard::unloadImages() {
	#ifdef CLIENT
	std::cout<<"Unloading wizard images..\n";
	SDL_FreeSurface(south_);
	SDL_FreeSurface(north_);
	SDL_FreeSurface(west_);
	SDL_FreeSurface(east_);
	for (unsigned int i = 0; i < 8; i++) {
		SDL_FreeSurface(walkSouth_[i]);
	}
	for (unsigned int i = 0; i < 8; i++) {
		SDL_FreeSurface(walkNorth_[i]);
	}
	for (unsigned int i = 0; i < 8; i++) {
		SDL_FreeSurface(walkWest_[i]);
	}
	for (unsigned int i = 0; i < 8; i++) {
		SDL_FreeSurface(walkEast_[i]);
	}
	#endif
}

void Wizard::calculatePathsRecur(unsigned int** const distances,
                                 const unsigned int x,
                                 const unsigned int y,
                                 const Direction newDirection,
                                 const unsigned int newDistance) {
	// update
	directions_[x][y] = newDirection;
	distances[x][y] = newDistance;
	// recur
	signed int dHeight;
	unsigned int cost;
	// if there is a tile to the north
	if ((y > 0) && (map->getTile(1, x, y - 1) == 0)) {
		// calculate movement cost (by an arbitrary formula)
		dHeight = ((signed int)map->getTile(0, x, y - 1) -
		           (signed int)map->getTile(0, x, y));
		cost = ((unsigned int)(dHeight * dHeight) + dHeight + 100);
		// if via this tile is the first or fastest route
		if ((distances[x][y] >= cost) &&
		    ((directions_[x][y - 1] == NO_DIRECTION) ||
		     ((distances[x][y] - cost) > distances[x][y - 1]))) {
			// recur
			calculatePathsRecur(distances, x, y - 1, NORTH,
			                    distances[x][y] - cost);
		}
	}
	// if there is a tile to the east
	if ((x < (map->getWidth() - 1)) && (map->getTile(1, x + 1, y) == 0)) {
		// calculate movement cost (by an arbitrary formula)
		dHeight = ((signed int)map->getTile(0, x + 1, y) -
		           (signed int)map->getTile(0, x, y));
		cost = ((unsigned int)(dHeight * dHeight) + dHeight + 100);
		// if via this tile is the first or fastest route
		if ((distances[x][y] >= cost) &&
		    ((directions_[x + 1][y] == NO_DIRECTION) ||
		     ((distances[x][y] - cost) > distances[x + 1][y]))) {
			// recur
			calculatePathsRecur(distances, x + 1, y, EAST,
			                    distances[x][y] - cost);
		}
	}
	// if there is a tile to the south
	if ((y < (map->getHeight() - 1)) && (map->getTile(1, x, y + 1) == 0)) {
		// calculate movement cost (by an arbitrary formula)
		dHeight = ((signed int)map->getTile(0, x, y + 1) -
		           (signed int)map->getTile(0, x, y));
		cost = ((unsigned int)(dHeight * dHeight) + dHeight + 100);
		// if via this tile is the first or fastest route
		if ((distances[x][y] >= cost) &&
		    ((directions_[x][y + 1] == NO_DIRECTION) ||
		     ((distances[x][y] - cost) > distances[x][y + 1]))) {
			// recur
			calculatePathsRecur(distances, x, y + 1, SOUTH,
			                    distances[x][y] - cost);
		}
	}
	// if there is a tile to the west
	if ((x > 0) && (map->getTile(1, x - 1, y) == 0)) {
		// calculate movement cost (by an arbitrary formula)
		dHeight = ((signed int)map->getTile(0, x - 1, y) -
		           (signed int)map->getTile(0, x, y));
		cost = ((unsigned int)(dHeight * dHeight) + dHeight + 100);
		// if via this tile is the first or fastest route
		if ((distances[x][y] >= cost) &&
		    ((directions_[x - 1][y] == NO_DIRECTION) ||
		     ((distances[x][y] - cost) > distances[x - 1][y]))) {
			// recur
			calculatePathsRecur(distances, x - 1, y, WEST,
			                    distances[x][y] - cost);
		}
	}
}

bool Wizard::getHasCast() {
	return hasCast_;
}

void Wizard::setHasCast(bool hasCast) {
	hasCast_ = hasCast;
}

bool Wizard::getHasMoved() {
	return hasMoved_;
}

void Wizard::setHasMoved(bool hasMoved) {
	hasMoved_ = hasMoved;
}

unsigned int Wizard::getClientId() {
	return clientId_;
}

bool Wizard::getIsMyTurn() {
	return isMyTurn_;
}

void Wizard::setIsMyTurn(bool isMyTurn) {
	isMyTurn_ = isMyTurn;
}

bool Wizard::getIsAlive() {
	return isAlive_;
}

void Wizard::setIsAlive(bool isAlive) {
	isAlive_ = isAlive;
}

